Passed
Push — main ( 9ab29e...5f4a75 )
by Eduardo
02:10
created

index.test.ts ➔ zipBack   F

Complexity

Conditions 25

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 7
rs 0
c 0
b 0
f 0
cc 25

How to fix   Complexity   

Complexity

Complex classes like index.test.ts ➔ zipBack often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/* eslint-disable @typescript-eslint/no-var-requires */
2
/** global: jest */
3
import { readFileSync } from 'fs'
4
import JSZip, { loadAsync } from 'jszip'
5
import { join } from 'path'
6
import { SoFloC } from '.'
7
import expectedZipCopy from './_jest/expectedZipCopy'
8
import expectedZipDelete from './_jest/expectedZipDelete'
9
const crypto = require('crypto')
10
11
jest.setTimeout(10 * 60 * 1000)
12
13
describe('SoFloC', () => {
14
  let file: Buffer
15
  const name = 'TestSolution_2_0_0_0.zip'
16
  const path = join(__dirname, '_jest', name)
17
18
  beforeEach(() => {
19
    jest.mock('crypto')
20
    crypto.randomUUID = jest.fn()
21
      .mockReturnValueOnce('01234567-xxxx-yyyy-zzzz-987654321098')
22
      .mockReturnValueOnce('12345678-yyyy-zzzz-xxxx-876543210987')
23
      .mockReturnValueOnce('23456789-zzzz-xxxx-yyyy-765432109876')
24
    file = readFileSync(path)
25
  })
26
  describe('copyFlow', () => {
27
    test('happy path', async () => {
28
      const sofloc = new SoFloC(file, name)
29
      await sofloc.copyFlow('f4910f26-8210-ec11-b6e6-002248842287', 'First Copy Flow', '2.1.0.0')
30
      await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'Second Copy Flow')
31
      await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'Third Copy Flow')
32
33
      const zip = await loadAsync(sofloc.data, { base64: true })
34
      expect(zip.files).toMatchObject(expectedZipCopy)
35
36
      expect(sofloc.name).toEqual('TestSolution_2_1_0_0.zip')
37
      expect(sofloc.version).toEqual('2.1.0.0')
38
39
      for (const key in zip.files) {
40
        if (Object.prototype.hasOwnProperty.call(zip.files, key)) {
41
          if (key !== 'Workflows/') {
42
            const file = zip.files[key]
43
            const data = await file.async('string')
44
45
            const expectedData = readFileSync(join(__dirname, '_jest', 'unzipedCopy', key)).toString()
46
            expect(cleanLineBreak(data)).toEqual(cleanLineBreak(expectedData))
47
          }
48
        }
49
      }
50
51
      expect(sofloc.workflows).toEqual([
52
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
53
        { id: '23456789-zzzz-xxxx-yyyy-765432109876', name: 'Third Copy Flow' },
54
        { id: '12345678-yyyy-zzzz-xxxx-876543210987', name: 'Second Copy Flow' },
55
        { id: 'f4910f26-8210-ec11-b6e6-002248842287', name: 'Second Test Flow' },
56
        { id: '01234567-xxxx-yyyy-zzzz-987654321098', name: 'First Copy Flow' },
57
      ])
58
    })
59
    test('failed to load', async () => {
60
      const sofloc = new SoFloC(null as any, null as any)
61
      let err: any
62
63
      try {
64
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
65
      } catch (error) {
66
        err = error
67
      }
68
      expect(err.message).toBe('Failed to unzip the file')
69
    })
70
    test('Workflow not on the solution', async () => {
71
      const zip = await loadAsync(file, { base64: true })
72
      delete zip.files['Workflows/FirstTestFlow-0F48CBA9-EF0C-ED11-82E4-000D3A64F6F2.json']
73
74
      const data = await zipBack(zip)
75
76
      const sofloc = new SoFloC(data, name)
77
78
      let err: any
79
      try {
80
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
81
      } catch (error) {
82
        err = error
83
      }
84
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
85
    })
86
    test('GUID not found on solution.xml', async () => {
87
      const zip = await loadAsync(file, { base64: true })
88
      let solution = await zip.files['solution.xml'].async('string')
89
      solution = solution.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
90
      zip.file('solution.xml', solution)
91
92
      const data = await zipBack(zip)
93
94
      const sofloc = new SoFloC(data, name)
95
96
      let err: any
97
      try {
98
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
99
      } catch (error) {
100
        err = error
101
      }
102
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
103
    })
104
    test('GUID not found on customizations.xml', async () => {
105
      const zip = await loadAsync(file, { base64: true })
106
      let customisations = await zip.files['customizations.xml'].async('string')
107
      customisations = customisations.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
108
      zip.file('customizations.xml', customisations)
109
110
      const data = await zipBack(zip)
111
112
      const sofloc = new SoFloC(data, name)
113
114
      let err: any
115
      try {
116
        await sofloc.copyFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', 'First Copy Flow')
117
      } catch (error) {
118
        err = error
119
      }
120
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
121
    })
122
  })
123
  describe('deleteFlow', () => {
124
    test('happy path', async () => {
125
      const sofloc = new SoFloC(file, name)
126
      await sofloc.deleteFlow('f4910f26-8210-ec11-b6e6-002248842287')
127
128
      const zip = await loadAsync(sofloc.data, { base64: true })
129
      expect(zip.files).toMatchObject(expectedZipDelete)
130
131
      expect(sofloc.name).toEqual('TestSolution_2_0_0_0.zip')
132
      expect(sofloc.version).toEqual('2.0.0.0')
133
134
      for (const key in zip.files) {
135
        if (Object.prototype.hasOwnProperty.call(zip.files, key)) {
136
          if (key !== 'Workflows/') {
137
            const file = zip.files[key]
138
            const data = await file.async('string')
139
140
            const expectedData = readFileSync(join(__dirname, '_jest', 'unzipedDelete', key)).toString()
141
            expect(cleanLineBreak(data)).toEqual(cleanLineBreak(expectedData))
142
          }
143
        }
144
      }
145
146
      expect(sofloc.workflows).toEqual([
147
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
148
      ])
149
    })
150
    test('failed to load', async () => {
151
      const sofloc = new SoFloC(null as any, null as any)
152
      let err: any
153
154
      try {
155
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
156
      } catch (error) {
157
        err = error
158
      }
159
      expect(err.message).toBe('Failed to unzip the file')
160
    })
161
    test('Workflow not on the solution', async () => {
162
      const zip = await loadAsync(file, { base64: true })
163
      delete zip.files['Workflows/FirstTestFlow-0F48CBA9-EF0C-ED11-82E4-000D3A64F6F2.json']
164
165
      const data = await zipBack(zip)
166
167
      const sofloc = new SoFloC(data, name)
168
169
      let err: any
170
      try {
171
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
172
      } catch (error) {
173
        err = error
174
      }
175
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
176
    })
177
    test('GUID not found on solution.xml', async () => {
178
      const zip = await loadAsync(file, { base64: true })
179
      let solution = await zip.files['solution.xml'].async('string')
180
      solution = solution.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
181
      zip.file('solution.xml', solution)
182
183
      const data = await zipBack(zip)
184
185
      const sofloc = new SoFloC(data, name)
186
187
      let err: any
188
      try {
189
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
190
      } catch (error) {
191
        err = error
192
      }
193
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
194
    })
195
    test('GUID not found on customizations.xml', async () => {
196
      const zip = await loadAsync(file, { base64: true })
197
      let customisations = await zip.files['customizations.xml'].async('string')
198
      customisations = customisations.replace('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', '000000000-aaaa-bbbb-cccc-000000000000')
199
      zip.file('customizations.xml', customisations)
200
201
      const data = await zipBack(zip)
202
203
      const sofloc = new SoFloC(data, name)
204
205
      let err: any
206
      try {
207
        await sofloc.deleteFlow('0f48cba9-ef0c-ed11-82e4-000d3a64f6f2')
208
      } catch (error) {
209
        err = error
210
      }
211
      expect(err.message).toBe("Workflow file with GUID '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2' does not exist in this Solution or the Solution was changed without updating 'solution.xml' or 'customizations.xml'")
212
    })
213
  })
214
  describe('updateVersion', () => {
215
    test('invalid version', async () => {
216
      const sofloc = new SoFloC(file, name)
217
218
      let err: any
219
      try {
220
        await sofloc.updateVersion('1.7.')
221
      } catch (error) {
222
        err = error
223
      }
224
      expect(err.message).toBe('Version \'1.7.\' is not valid. It should follow the format <major>.<minor>.<?build>.<?revision>.')
225
      expect(sofloc.version).toBe('2.0.0.0')
226
    })
227
    test('smaller version', async () => {
228
      const sofloc = new SoFloC(file, name)
229
230
      let err: any
231
      try {
232
        await sofloc.updateVersion('1.99.99.99')
233
      } catch (error) {
234
        err = error
235
      }
236
      expect(err.message).toBe('Version \'1.99.99.99\' is smaller than \'2.0.0.0\'')
237
      expect(sofloc.version).toBe('2.0.0.0')
238
    })
239
    test('smaller version with less chars', async () => {
240
      const sofloc = new SoFloC(file, name)
241
242
      let err: any
243
      try {
244
        await sofloc.updateVersion('1.9')
245
      } catch (error) {
246
        err = error
247
      }
248
      expect(err.message).toBe('Version \'1.9\' is smaller than \'2.0.0.0\'')
249
      expect(sofloc.version).toBe('2.0.0.0')
250
    })
251
    test('bigger version with less chars', async () => {
252
      const sofloc = new SoFloC(file, name)
253
254
      let err: any
255
      try {
256
        await sofloc.updateVersion('2.1')
257
      } catch (error) {
258
        err = error
259
      }
260
      expect(err).toBeUndefined()
261
      expect(sofloc.version).toBe('2.1')
262
    })
263
    test('same version with less chars', async () => {
264
      const sofloc = new SoFloC(file, name)
265
266
      let err: any
267
      try {
268
        await sofloc.updateVersion('2.0')
269
      } catch (error) {
270
        err = error
271
      }
272
      expect(sofloc.version).toBe('2.0.0.0')
273
      expect(err.message).toBe('Version \'2.0\' is smaller than \'2.0.0.0\'')
274
    })
275
    test('same version', async () => {
276
      const sofloc = new SoFloC(file, name)
277
278
      let err: any
279
      try {
280
        await sofloc.updateVersion('2.0.0.0')
281
      } catch (error) {
282
        err = error
283
      }
284
      expect(err).toBeUndefined()
285
      expect(sofloc.version).toBe('2.0.0.0')
286
    })
287
    test('bigger version then smaller, but bigger than original', async () => {
288
      const sofloc = new SoFloC(file, name)
289
290
      let err: any
291
      try {
292
        await sofloc.updateVersion('2.3')
293
        await sofloc.updateVersion('2.1.0.0')
294
      } catch (error) {
295
        err = error
296
      }
297
      expect(err).toBeUndefined()
298
      expect(sofloc.version).toBe('2.1.0.0')
299
    })
300
  })
301
  describe('get workflows', () => {
302
    test('not loaded', async () => {
303
      const sofloc = new SoFloC(file, name)
304
305
      const wfs = sofloc.workflows
306
      expect(wfs).toEqual([])
307
    })
308
  })
309
  describe('data', () => {
310
    test('version is updated', async () => {
311
      const sofloc = new SoFloC(file, name)
312
      await sofloc.updateVersion('2.1.0.0')
313
314
      const zip = await JSZip.loadAsync(sofloc.data, { base64: true })
315
      const solution = await zip.files['solution.xml'].async('string')
316
      const version = solution.match(/<Version>(.*)<\/Version>/) as any[]
317
318
      expect(version[1]).toBe('2.1.0.0')
319
      expect(sofloc.workflows).toEqual([
320
        { id: '0f48cba9-ef0c-ed11-82e4-000d3a64f6f2', name: 'First Test Flow' },
321
        { id: 'f4910f26-8210-ec11-b6e6-002248842287', name: 'Second Test Flow' },
322
      ])
323
    })
324
  })
325
  describe('#load', () => {
326
    test('version not found on Solution)', async () => {
327
      const zip = await loadAsync(file, { base64: true })
328
      let solution = await zip.files['solution.xml'].async('string')
329
      solution = solution.replace('<Version>2.0.0.0</Version>', '')
330
      zip.file('solution.xml', solution)
331
332
      const data = await zipBack(zip)
333
      const sofloc = new SoFloC(data, name)
334
335
      let err: any
336
      try {
337
        await sofloc.updateVersion('2.1.0.0')
338
      } catch (error) {
339
        err = error
340
      }
341
      expect(err.message).toBe('Failed to retrieve the version')
342
    })
343
    test('solution.xml not found', async () => {
344
      const zip = await loadAsync(file, { base64: true })
345
      delete zip.files['solution.xml']
346
347
      const data = await zipBack(zip)
348
      const sofloc = new SoFloC(data, name)
349
350
      let err: any
351
      try {
352
        await sofloc.updateVersion('2.1.0.0')
353
      } catch (error) {
354
        err = error
355
      }
356
      expect(err.message).toBe("'solution.xml' was not found in the Solution zip")
357
    })
358
    test('customizations.xml not found', async () => {
359
      const zip = await loadAsync(file, { base64: true })
360
      delete zip.files['customizations.xml']
361
362
      const data = await zipBack(zip)
363
      const sofloc = new SoFloC(data, name)
364
365
      let err: any
366
      try {
367
        await sofloc.updateVersion('2.1.0.0')
368
      } catch (error) {
369
        err = error
370
      }
371
      expect(err.message).toBe("'customizations.xml' was not found in the Solution zip")
372
    })
373
  })
374
})
375
376
async function zipBack (zip: JSZip) {
377
  return await zip.generateAsync({
378
    type:               'base64',
379
    compression:        'DEFLATE',
380
    compressionOptions: {
381
      level: 9,
382
    },
383
  })
384
}
385
386
function cleanLineBreak (content: string) {
387
  return content.replace(/\r\n/gm, '\n')
388
}
389